/********************************************************************
	created:	2:10:2007   20:42
	author:		tiamo
	purpose:	translate usage
*********************************************************************/

#include "stdafx.h"

// lookup
typedef ULONG (*fnLookup)(__in PVOID pTablePointer,__in ULONG value);

// translate
typedef BOOLEAN (*fnTranslateRoutine)(__in PULONG pTablePointer,__in ULONG scancode,
									  __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
									  __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState);

BOOLEAN AppleKeyboardKeypadCode(__in PULONG pTablePointer,__in ULONG scancode,
								__in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
								__in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState);

BOOLEAN AppleModifierCode(__in PULONG pTablePointer,__in ULONG scancode,
						  __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
						  __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState);

BOOLEAN AppleVendorBreakCodesAsMakeCodes(__in PULONG pTablePointer,__in ULONG scancode,
										 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
										 __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState);

BOOLEAN ApplePrintScreenCode(__in PULONG pTablePointer,__in ULONG scancode,
							 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
							 __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState);

// usage -> scancode
struct CUsageToScancode
{
	// usage
	ULONG																m_ulUsage;
	// scan code
	ULONG																m_ulScancode;
};

// sub table item
struct CSubTableItem
{
	// routine
	fnTranslateRoutine													m_fnTranslate;
	// table
	PULONG																m_pTranslateTable;
};

// consumer table
CUsageToScancode														g_appleConsumerToScanCodeTable[255] = 
{
	// scan next track
	{0xb5,0x19e0},
	// scan prev track
	{0xb6,0x10e0},
	// stop
	{0xb7,0x24e0},
	// play/pause
	{0xcd,0x22e0},
	// mute
	{0xe2,0x20e0},
	// volume ++
	{0xe9,0x30e0},
	// volume --
	{0xea,0x2ee0},
	// media select
	{0x183,0x6de0},
	// email
	{0x18a,0x6ce0},
	// my computer
	{0x194,0x6be0},
	// calculator
	{0x192,0x21e0},
	// www search
	{0x221,0x65e0},
	// www home
	{0x223,0x32e0},
	// www back
	{0x224,0x6ae0},
	// www forward
	{0x225,0x69e0},
	// www stop
	{0x226,0x68e0},
	// www flush
	{0x227,0x67e0},
	// www bookmark
	{0x22a,0x66e0},
};

// consumer sub table
CSubTableItem															g_appleConsumerSubTables[255] = {0};

// keyboard
ULONG																	g_appleXlateKbdPadCodesSubTable[] = 
{
	// insert
	0x52e0,
	// home
	0x47e0,
	// page up
	0x49e0,
	// delete
	0x53e0,
	// end
	0x4fe0,
	// page down
	0x51e0,
	// right arrow
	0x4de0,
	// left arrow
	0x4be0,
	// down arrow
	0x50e0,
	// up arrow
	0x48e0,
};

// modifier
ULONG																	g_appleXlateModifierCodesSubTable[] = 
{
	0x1D,0x2A,0x38,0x5BE0,0x1DE0,0x36,0x38E0,0x5CE0,0x3A,0x46,0x45
};

// vendor
ULONG																	g_appleBreakCodesAsMakeCodesTable[] = {0xf2,0xf1};

// screen print
ULONG																	g_appleXlatePrtScrCodesSubTable[] = {0x37e0};

// keyboard sub table
CSubTableItem															g_appleKeyboardSubTables[255] = 
{
	{AppleKeyboardKeypadCode,g_appleXlateKbdPadCodesSubTable},
	{AppleModifierCode,g_appleXlateModifierCodesSubTable},
	{AppleVendorBreakCodesAsMakeCodes,g_appleBreakCodesAsMakeCodesTable},
	{ApplePrintScreenCode,g_appleXlatePrtScrCodesSubTable},
};

// keyboard to scancode
ULONG																	g_appleKeyboardToScanCodeTable[256] = 
{
		0xff,
		0xff,
		0xff,
		0xff,
		0x1e,			// a
		0x30,
		0x2e,
		0x20,
		0x12,
		0x21,
		0x22,
		0x23,
		0x17,
		0x24,
		0x25,
		0x26,
		0x32,
		0x31,
		0x18,
		0x19,
		0x10,
		0x13,
		0x1f,
		0x14,
		0x16,
		0x2f,
		0x11,
		0x2d,
		0x15,
		0x2c,			// z
		0x02,			// 1
		0x03,
		0x04,
		0x05,
		0x06,
		0x07,
		0x08,
		0x09,
		0x0a,
		0x0b,			// 0
		0x1c,			// enter 
		0x01,			// esc
		0x0e,			// backspace
		0x0f,			// tab
		0x39,			// sapce
		0x0c,			// -
		0x0d,			// =
		0x1a,			// [
		0x1b,			// ]
		0x2b,			// |
		0x2b,			// europe 1
		0x27,			// ;
		0x28,			// '
		0x29,			// `
		0x33,			// ,
		0x34,			// .
		0x35,			// ?
		0x8f1,			// caps lock,sub table = 1,index = 8
		0x3b,			// f1
		0x3c,
		0x3d,
		0x3e,
		0x3f,
		0x40,
		0x41,
		0x42,
		0x43,
		0x44,
		0x57,
		0x58,			// f12
		0x0f3,			// print screen,sub table 3 index = 0
		0x9f1,			// scroll lock,sub table = 1 index = 9
		0x451de1,		// break,
		0x0f0,			// insert
		0x1f0,			// home
		0x2f0,			// page up
		0x3f0,			// delete
		0x4f0,			// end
		0x5f0,			// page down
		0x6f0,			// right arrow
		0x7f0,			// left arrow
		0x8f0,			// down arrow
		0x9f0,			// up arrow
		0x0af1,			// num lock
		0x35e0,			// keypad /
		0x37,			// keypad *
		0x4a,			// keypad -
		0x4e,			// keypad +
		0x1ce0,			// keypad enter
		0x4f,			// keypad 1 end
		0x50,			// keypad 2 down
		0x51,			// keypad 3 page down
		0x4b,			// keypad 4 left
		0x4c,			// keypad 5
		0x4d,			// keypad 6 right
		0x47,			// keypad 7 home
		0x48,			// keypad 8 up
		0x49,			// keypad 9 page up
		0x52,			// keypad 0 insert
		0x53,			// keypad. delete
		0x56,			// europe 2
		0x5de0,			// app
		0x5ee0,			// keyboard power
		0x59,			// keypad =
		0x64,			// f13
		0x65,
		0x66,
		0x67,
		0x68,
		0x69,
		0x6a,
		0x6b,
		0x6c,
		0x6d,
		0x6e,
		0x76,			// f24
		0xff,			// keypad exec
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0x7e,		// keypad ,
		0xff,
		0x73,		// keyboard ro
		0x70,		// keyboard katakana-hiragana
		0x7d,		// keyboard yen
		0x79,		// keyboard henkan
		0x7b,		// keyboard muhenkan
		0x5c,		// keyboard international 6
		0xff,		// keyboard international 7
		0xff,
		0xff,
		0xf2,		// keyboard lang 1
		0x1f2,		// keyboard lang 2
		0x78,		// katakana
		0x77,		// hiragana
		0x76,		// zenkaku/hankaku
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0x0f1,			// left control
		0x1f1,			// left shift
		0x2f1,			// left alt
		0x3f1,			// left gui
		0x4f1,			// right control
		0x5f1,			// right shift
		0x6f1,			// right alt
		0x7f1,			// right gui
		0xff,
		0x5ee0,			// system power,page = 1
		0x5fe0,			// system sleep,page = 1
		0x63e0,			// system wake,page = 1
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
		0xff,
};

// associative lookup
ULONG AppleAssociativeLookup(__in PVOID pTablePointer,__in ULONG value)
{
	CUsageToScancode* pTable = static_cast<CUsageToScancode*>(pTablePointer);
	while(pTable->m_ulUsage)
	{
		if(pTable->m_ulUsage == value)
			return pTable->m_ulScancode;

		pTable ++;
	}

	return 0;
}

// staight lookup
ULONG AppleStraightLookup(__in PVOID pTablePointer,__in ULONG value)
{
	if(value < 0xff)
		return *(static_cast<PULONG>(pTablePointer) + value);

	return 0;
}

// put key
BOOLEAN AppleKeyboardPutKey(__in ULONG scancode,__in HIDP_KEYBOARD_DIRECTION keyAction,
							__in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext)
{
	PUCHAR pScancode = static_cast<PUCHAR>(static_cast<PVOID>(&scancode));

	ULONG i;
	for(i = 0; i < sizeof(ULONG); i++)
	{
		if(!pScancode[i])
			break;

		if(keyAction == HidP_Keyboard_Break)
			pScancode[i] |= 0x80;
	}

	if(i)
		pInsertCodesProcedure(pInsertCodesContext,static_cast<PCHAR>(static_cast<PVOID>(&scancode)),i);

	return TRUE;
}

// translate usage
NTSTATUS AppleTranslateUsage(__in USAGE usage,__in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState,
							 __in fnLookup pfnLookup,__in PVOID pTablePointer,__in CSubTableItem* pSubTable,
							 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext)
{
	ULONG scancode = pfnLookup(pTablePointer,usage);
	if(!scancode)
		return HIDP_STATUS_I8042_TRANS_UNKNOWN;

	// ctrl + pause,break
	if((pModifierState->LeftControl || pModifierState->RightControl) && scancode == 0x451DE1)
		scancode = 0x46E0;

	if((scancode & 0xf0) == 0xf0)
	{
		ULONG index = scancode & 0xf;
		if(!pSubTable[index].m_fnTranslate)
			return HIDP_STATUS_I8042_TRANS_UNKNOWN;

		scancode >>= 8;
		if(!pSubTable[index].m_fnTranslate(pSubTable[index].m_pTranslateTable,scancode,pInsertCodesProcedure,pInsertCodesContext,keyAction,pModifierState))
			return HIDP_STATUS_I8042_TRANS_UNKNOWN;
	}
	else
	{
		AppleKeyboardPutKey(scancode,keyAction,pInsertCodesProcedure,pInsertCodesContext);
	}

	return HIDP_STATUS_SUCCESS;
}

// tranlate to 8024scan codes
NTSTATUS AppleKeyboardTranslateUsageAndPagesToI8042ScanCodes(__in PUSAGE_AND_PAGE pChangedUsageList,__in ULONG ulUsageListLength,
															 __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState,
															 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext)
{
	NTSTATUS status = HIDP_STATUS_SUCCESS;

	while(ulUsageListLength)
	{
		if(pChangedUsageList->UsagePage == HID_USAGE_PAGE_KEYBOARD)
		{
			status = AppleTranslateUsage(pChangedUsageList->Usage,keyAction,pModifierState,&AppleStraightLookup,
										 g_appleKeyboardToScanCodeTable,g_appleKeyboardSubTables,pInsertCodesProcedure,pInsertCodesContext);
		}
		else if(pChangedUsageList->UsagePage == HID_USAGE_PAGE_CONSUMER)
		{
			status = AppleTranslateUsage(pChangedUsageList->Usage,keyAction,pModifierState,&AppleAssociativeLookup,
										 g_appleConsumerToScanCodeTable,g_appleConsumerSubTables,pInsertCodesProcedure,pInsertCodesContext);
	
		#if 0
			if(pChangedUsageList->Usage == 0xb8)
			{
				if(keyAction == HidP_Keyboard_Make)
				{
					// +ctrl
					AppleKeyboardPutKey(0x1d,HidP_Keyboard_Make,pInsertCodesProcedure,pInsertCodesContext);
					// +v
					AppleKeyboardPutKey(0x2f,HidP_Keyboard_Make,pInsertCodesProcedure,pInsertCodesContext);
				}
				else
				{
					// -v
					AppleKeyboardPutKey(0x2f,HidP_Keyboard_Break,pInsertCodesProcedure,pInsertCodesContext);
					// -ctrl
					AppleKeyboardPutKey(0x1d,HidP_Keyboard_Break,pInsertCodesProcedure,pInsertCodesContext);
				}
			}
		#endif
		}
		else
		{
			return HIDP_STATUS_I8042_TRANS_UNKNOWN;
		}

		if(status != HIDP_STATUS_SUCCESS)
			break;

		ulUsageListLength --;
		pChangedUsageList ++;
	}

	return status;
}

// keyboard keypad
BOOLEAN AppleKeyboardKeypadCode(__in PULONG pTablePointer,__in ULONG subTableIndex,
								__in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
								__in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState)
{
	// why do not follow scancode.doc note 1?
	if(pModifierState->NumLock && keyAction == HidP_Keyboard_Make)
		if(!AppleKeyboardPutKey(0x2ae0,HidP_Keyboard_Make,pInsertCodesProcedure,pInsertCodesContext))
			return FALSE;

	if(!AppleKeyboardPutKey(pTablePointer[subTableIndex],keyAction,pInsertCodesProcedure,pInsertCodesContext))
		return FALSE;

	if(pModifierState->NumLock && keyAction == HidP_Keyboard_Break)
		if(!AppleKeyboardPutKey(0x2ae0,HidP_Keyboard_Break,pInsertCodesProcedure,pInsertCodesContext))
			return FALSE;

	return FALSE;
}

// modifier
BOOLEAN AppleModifierCode(__in PULONG pTablePointer,__in ULONG subTableIndex,
						  __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
						  __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState)
{
	do
	{
		ULONG ulIndex = subTableIndex;

		// special for num,scroll,caps lock
		if(subTableIndex >= 8)
		{
			if(keyAction == HidP_Keyboard_Make)
			{
				// first time make flags
				if(!(pModifierState->ul & (1 << (subTableIndex + 16))))
				{
					// set flags
					pModifierState->ul |= (1 << (subTableIndex + 16));
					// toggle key
					pModifierState->ul ^= (1 << subTableIndex);
				}

				break;
			}
			else
			{
				// just clear hiword flags
				ulIndex += 16;
			}
		}

		if(keyAction == HidP_Keyboard_Make)
			pModifierState->ul |= (1 << ulIndex);
		else
			pModifierState->ul &= ~(1 << ulIndex);

	}while(0);

	return AppleKeyboardPutKey(pTablePointer[subTableIndex],keyAction,pInsertCodesProcedure,pInsertCodesContext);
}

// vendor,LANG1 and LANG2
BOOLEAN AppleVendorBreakCodesAsMakeCodes(__in PULONG pTablePointer,__in ULONG subTableIndex,
										 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
										 __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState)
{
	if(keyAction == HidP_Keyboard_Break)
		return TRUE;

	if(keyAction == HidP_Keyboard_Make)
		return AppleKeyboardPutKey(pTablePointer[subTableIndex],HidP_Keyboard_Make,pInsertCodesProcedure,pInsertCodesContext);

	return FALSE;
}

// print screen
BOOLEAN ApplePrintScreenCode(__in PULONG pTablePointer,__in ULONG subTableIndex,
							 __in PHIDP_INSERT_SCANCODES pInsertCodesProcedure,__in PVOID pInsertCodesContext,
							 __in HIDP_KEYBOARD_DIRECTION keyAction,__in __out PHIDP_KEYBOARD_MODIFIER_STATE pModifierState)
{
	// scancode.doc note 4

	// 54 | d4
	if(pModifierState->LeftAlt || pModifierState->RightAlt)
		return AppleKeyboardPutKey(0x54,keyAction,pInsertCodesProcedure,pInsertCodesContext);

	// e0 37 | e0 b7
	if( (pModifierState->LeftControl && pModifierState->LeftShift) ||
		(pModifierState->RightControl && pModifierState->RightShift))
	{
		return AppleKeyboardPutKey(pTablePointer[subTableIndex],keyAction,pInsertCodesProcedure,pInsertCodesContext);
	}

	if(keyAction == HidP_Keyboard_Break)
	{
		// e0 37 e0 aa
		if(!AppleKeyboardPutKey(pTablePointer[subTableIndex],keyAction,pInsertCodesProcedure,pInsertCodesContext))
			return FALSE;

		return AppleKeyboardPutKey(0x2ae0,HidP_Keyboard_Break,pInsertCodesProcedure,pInsertCodesContext);
	}

	// e0 2a e0 37
	if(!AppleKeyboardPutKey(0x2ae0,HidP_Keyboard_Make,pInsertCodesProcedure,pInsertCodesContext))
		return FALSE;

	return AppleKeyboardPutKey(pTablePointer[subTableIndex],keyAction,pInsertCodesProcedure,pInsertCodesContext);
}